﻿using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Core.HttpHelper
{
    public static class HttpContextHelper
    {
        private static Func<object> _asyncLocalAccessor;
        private static Func<object, object> _holderAccessor;
        private static Func<object, HttpContext> _httpContextAccessor;
        /// <summary>
        /// 获取当前的httpcontext
        /// </summary>
        /// <returns></returns>
        public static HttpContext GetCurrentHttpContext()
        {
            var asyncLocal = (_asyncLocalAccessor ??= CreateAsyncLocalAccessor())();
            if (asyncLocal == null)
            {
                return null;
            }

            var holder = (_holderAccessor ??= CreateHolderAccessor(asyncLocal))(asyncLocal);
            if (holder == null)
            {
                return null;
            }

            return (_httpContextAccessor ??= CreateHttpContextAccessor(holder))(holder);

            static Func<object> CreateAsyncLocalAccessor()
            {
                var fieldInfo = typeof(HttpContextAccessor).GetField("_httpContextCurrent", BindingFlags.Static | BindingFlags.NonPublic);
                var field = Expression.Field(null, fieldInfo);
                return Expression.Lambda<Func<object>>(field).Compile();
            }

            static Func<object, object> CreateHolderAccessor(object asyncLocal)
            {
                var holderType = asyncLocal.GetType().GetGenericArguments()[0];
                var method = typeof(AsyncLocal<>).MakeGenericType(holderType).GetProperty("Value").GetGetMethod();
                var target = Expression.Parameter(typeof(object));
                var convert = Expression.Convert(target, asyncLocal.GetType());
                var getValue = Expression.Call(convert, method);
                return Expression.Lambda<Func<object, object>>(getValue, target).Compile();
            }

            static Func<object, HttpContext> CreateHttpContextAccessor(object holder)
            {
                var target = Expression.Parameter(typeof(object));
                var convert = Expression.Convert(target, holder.GetType());
                var field = Expression.Field(convert, "Context");
                var convertAsResult = Expression.Convert(field, typeof(HttpContext));
                return Expression.Lambda<Func<object, HttpContext>>(convertAsResult, target).Compile();
            }
        }

        /// <summary>
        /// 获取路由终结点方法
        /// </summary>
        /// <param name="context">http上下文</param>
        /// <returns></returns>
        public static Endpoint GetEndPoint(HttpContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            return context.Features.Get<IEndpointFeature>()?.Endpoint;
        }

        /// <summary>
        /// 获取自定义特性类方法
        /// </summary>
        /// <typeparam name="T">自定义控制器特性类</typeparam>
        /// <param name="context">数据库上下文</param>
        /// <returns></returns>
        public static T GetMetadata<T>(HttpContext context) where T : class
        {
            Endpoint EndPoint = GetEndPoint(context);
            if (EndPoint == null)
            {
                return null;
            }
            return EndPoint.Metadata.GetMetadata<T>();
        }
        /// <summary>
        /// 获取http的返回值并返回一个Jobject的弱类型的实体
        /// </summary>
        /// <param name="httpResponse"></param>
        /// <returns></returns>
        public static async Task<JObject> GetResponse(HttpResponse httpResponse)
        {
            httpResponse.Body.Seek(0, SeekOrigin.Begin);
            var text = await new StreamReader(httpResponse.Body).ReadToEndAsync();
            httpResponse.Body.Seek(0, SeekOrigin.Begin);
            return JObject.Parse(text);
        }

        public static async Task<JObject> GetRequestBody(Stream s)
        {
            s.Seek(0, SeekOrigin.Begin);
            var text = await new StreamReader(s).ReadToEndAsync();
            s.Seek(0, SeekOrigin.Begin);
            return JObject.Parse(text);
        }
    }
}
